﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using UCS.XNA.Utils;
using DivideAndConquer.States.InGame.Events;

namespace DivideAndConquer.States.InGame
{
    public class InGameState : GameState
    {
        #region Private Members

        private List<OpenField> _openFields;
        private List<OpenField> _openFieldsAddQueue = new List<OpenField>();
        private List<OpenField> _openFieldsRemoveQueue = new List<OpenField>();
        private WallOrientation _createWallOrientation;
        private MouseState _lastMouseState;
        private Background _background;

        #endregion

        #region Contructors

        public InGameState()
        {
            Rectangle availableBounds = new Rectangle(0, 0, GameResources.Graphics.PreferredBackBufferWidth, GameResources.Graphics.PreferredBackBufferHeight);

            this.InitializeOpenFields(availableBounds);

            this.InitializeBackground(availableBounds);
        }

        #endregion

        #region Private Methods

        private void InitializeBackground(Rectangle availableBounds)
        {
            _background = new Background(availableBounds, GameResources.Content.Load<Texture2D>(@"Textures\Field"));
        }

        private void InitializeOpenFields(Rectangle availableBounds)
        {
            int x = Convert.ToInt32(availableBounds.X + availableBounds.Width * 0.1f);
            int y = Convert.ToInt32(availableBounds.Y + availableBounds.Height * 0.1f);
            int width = Convert.ToInt32(availableBounds.Width * 0.8f);
            int height = Convert.ToInt32(availableBounds.Height * 0.8f);

            _openFields = new List<OpenField>();

            var openFieldBounds = new Rectangle(x, y, width, height);
            var openField = new OpenField(openFieldBounds, this.CreateBalls(openFieldBounds, 2));

            EventHandler<OpenFieldDividedEventArgs> divided = null;
            divided = (sender, e) =>
                {
                    _openFieldsRemoveQueue.Add(e.OriginalOpenField);

                    if (e.NewOpenFieldA != null)
                    {
                        _openFieldsAddQueue.Add(e.NewOpenFieldA);
                        e.NewOpenFieldA.CreateWallOrientation = _createWallOrientation;
                        e.NewOpenFieldA.Divided += divided;
                    }
                    if (e.NewOpenFieldB != null)
                    {
                        _openFieldsAddQueue.Add(e.NewOpenFieldB);
                        e.NewOpenFieldB.CreateWallOrientation = _createWallOrientation;
                        e.NewOpenFieldB.Divided += divided;
                    }
                };

            openField.Divided += divided;

            _openFields.Add(openField);
        }

        private void TestWallOrientationChange()
        {
            MouseState mouseState = Mouse.GetState();

            if (_lastMouseState != null && _lastMouseState.RightButton == ButtonState.Released && mouseState.RightButton == ButtonState.Pressed)
            {
                _createWallOrientation = _createWallOrientation == WallOrientation.Horizontal ? WallOrientation.Vertical : WallOrientation.Horizontal;

                foreach (var openField in _openFields)
                    openField.CreateWallOrientation = _createWallOrientation;
            }

            _lastMouseState = mouseState;
        }

        private List<Ball> CreateBalls(Rectangle bounds, int ballsCount)
        {
            List<Ball> result = new List<Ball>();
            Random random = new Random();

            for (int i = 0; i < ballsCount; i++)
            {
                var x = random.Next(bounds.Left + 4, bounds.Right - 4);
                var y = random.Next(bounds.Top + 4, bounds.Bottom - 4);
                var dx = random.Next(-100, 100) / 100f;
                var dy = random.Next(-100, 100) / 100f;
                var direction = new Vector2(dx, dy);

                Ball ball = new Ball(new Vector2(x, y));
                ball.Direction = direction;

                result.Add(ball);
            }

            return result;
        }

        #endregion

        #region Overriden Methods

        public override void Update(GameTime gameTime)
        {
            _openFieldsRemoveQueue.ForEach(o => _openFields.Remove(o));
            _openFieldsRemoveQueue.Clear();

            _openFields.AddRange(_openFieldsAddQueue);
            _openFieldsAddQueue.Clear();

            this.TestWallOrientationChange();

            foreach (var openField in _openFields)
                openField.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            if (_background != null)
                _background.Draw(gameTime);

            foreach (var openField in _openFields)
                openField.Draw(gameTime);
        }

        #endregion
    }
}